package com.mall4j.cloud.order.infrustructure.interceptor;


import com.mall4j.cloud.base.exception.BusinessException;
import com.mall4j.cloud.order.infrustructure.annotations.Idempotent;
import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.annotation.Around;
import org.aspectj.lang.annotation.Aspect;
import org.redisson.api.RBucket;
import org.redisson.api.RedissonClient;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;
import org.springframework.util.DigestUtils;
import java.nio.charset.StandardCharsets;
import java.util.Arrays;
import java.util.concurrent.TimeUnit;

@Aspect
@Component
public class IdempotentInterceptor {

    @Autowired
    private RedissonClient redissonClient;

    private static final String LOCK_PREFIX = "mall-order:idempotent:%s";

    @Around("@annotation(idempotent)")
    public Object around(ProceedingJoinPoint joinPoint, Idempotent idempotent) throws Throwable {
        String key = generateKey(joinPoint);
        RBucket<Boolean> bucket = redissonClient.getBucket(String.format(LOCK_PREFIX, key));
        if (bucket.isExists()) {
            throw new BusinessException("Duplicate request");
        } else {
            bucket.set(true, idempotent.expireMillis(), TimeUnit.MILLISECONDS);
            return joinPoint.proceed();
        }
    }

    private String generateKey(ProceedingJoinPoint joinPoint) {
        String methodName = joinPoint.getSignature().getName();
        Object[] args = joinPoint.getArgs();
        String argsHash = DigestUtils.md5DigestAsHex(Arrays.deepToString(args).getBytes(StandardCharsets.UTF_8));
        return methodName + ":" + argsHash;
    }
}

